using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;

namespace cs2335.lab5
{
    /// <summary>
    /// sdavis31 - Menu class - menu header plus list of menu items
    /// </summary>
    public class Menu : IComponent
    {
        protected Rectangle menuRectangle = new Rectangle(0, 0, 100, 20);
        protected StringFormat labelFormat = new StringFormat(StringFormatFlags.NoClip);
        protected SolidBrush menuBrush = new SolidBrush(Color.Ivory);
        protected Font font = new Font("SansSerif", 10);
        protected CCTForm owner;
        protected string text;
        protected bool hasFocus = false;
        protected bool hidden = false;
        protected string name = "Menu";

        protected List<MenuItem> menuItemList = new List<MenuItem>(); // sdavis31 - list of menu items
        protected bool open = false; // sdavis31 - is menu open, i.e. invoked
        protected int index;
        protected Rectangle container = new Rectangle(0, 0, 100, 20);
        protected Rectangle menusContainer = new Rectangle(0, 0, 100, 20);

        #region Properties
        /// <summary>
        /// Tells whether or not a component is hidden. If it is hidden,
        /// it should not be receiving actions.
        /// </summary>
        public bool Hidden
        {
            get { return hidden; }
        }

        /// <summary>
        /// sdavis31 - public access to the list of menu items
        /// </summary>
        public List<MenuItem> MenuItemList
        {
            get { return menuItemList; }
            set { menuItemList = value; Invalidate(); }
        }

        /// <summary>
        /// sdavis31 - public access to whether menu is open or invoked
        /// </summary>
        public bool Open
        {
            get { return open; }
            set { open = value; Invalidate(); }
        }

        public Point Location
        {
            get { return menuRectangle.Location; }
            set 
            {
                menuRectangle.Location = value;
                container.Location = value;
                menusContainer.Location = value;
                menusContainer.Y = container.Bottom;
                Invalidate(); 
            }
        }

        public Rectangle Bounds
        {
            get { return container; }
            set { container = value; Invalidate(); }
        }

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public System.Drawing.Font Font
        {
            get { return font; }
            set { font = value; Invalidate(); }
        }

        public string Text
        {
            get { return text; }
            set { text = value; Invalidate(); }
        }

        public CCTForm Owner
        {
            get { return owner; }
            set { owner = value; }
        }

        public Color BackColor
        {
            get { return menuBrush.Color; }
            set { menuBrush.Color = value; Invalidate(); }
        }
        #endregion

        #region Events
        public event EventHandler Click;
        public event EventHandler GotFocus;
        public event EventHandler KeyPress;
        public event EventHandler KeyDown;
        public event EventHandler LostFocus;
        public event EventHandler Paint;
        #endregion

        /// <summary>
        /// sdavis31 - instantiating a Menu object
        /// </summary>
        /// <param name="caption">parameter for name and text of menu</param>
        /// <param name="x">parameter for x coordinate in location</param>
        /// <param name="y">parameter for y coordinate in location</param>
        public Menu(string caption, int x, int y)
        {
            // sdavis31 - left align text
            labelFormat.LineAlignment = StringAlignment.Near;
            labelFormat.Alignment = StringAlignment.Near;

            text = caption;
            Location = new Point(x, y);

            // sdavis31 - adding the menu items and setting the number of items for the menu
            /*
            this.AddMenuItem("Happy Message", true, false, 1, x, y + 20);
            this.AddMenuItem("Sad Message", false, false, 2, x, y + 40);
            this.AddMenuItem("Ambivalent Message", false, true, 3, x, y + 60);
             * */
        }

        public Menu()
        {
            container = menuRectangle;
        }

        public Menu(string s)
        {
            text = s;
            container = menuRectangle;
        }

        public void Invalidate()
        {
            if (owner != null)
            {
                owner.Invalidate();
            }
        }

        public void Hide()
        {
            hidden = true;
            Invalidate();
        }

        public void Show()
        {
            hidden = false;
            Invalidate();
        }

        public void OnPaint(PaintEventArgs e)
        {
            if (!hidden)
            {
                int margin = 2;
                Graphics g = e.Graphics;
                if (!hasFocus)
                {
                    g.FillRectangle(menuBrush, menuRectangle);
                    //Draw3DBorder(g, menuRectangle);
                    g.DrawString(text, font, Brushes.Black, new PointF(menuRectangle.X + margin, menuRectangle.Y + margin));
                    foreach (MenuItem m in menuItemList)
                    {
                        if (m.HasFocus)
                        {
                            Draw3DBorder(g, menusContainer);
                            break;
                        }
                    }
                }
                if (hasFocus)
                {
                    DrawFocus(g);
                    if (open)
                    {
                        Draw3DBorder(g, menusContainer);
                        foreach (MenuItem m in MenuItemList)
                        {
                            m.OnPaint(e);
                        }
                    }
                }

                if (Paint != null)
                {
                    Paint(this, e);
                }
            }

        }

        public void OnGotFocus(EventArgs e)
        {

            hasFocus = true;
            if (GotFocus != null)
            {
                GotFocus(this, e);
            }
            owner.Invalidate();
        }

        public void OnLostFocus(EventArgs e)
        {
            hasFocus = false;
            if (LostFocus != null)
            {
                LostFocus(this, e);
            }
            owner.Invalidate();
        }

        public void OnMouseClick(System.Windows.Forms.MouseEventArgs e)
        {
            if (menuRectangle.Contains(e.Location))
            {
                index = 0;
                MenuSwap();
                hasFocus = true;
                //owner.MenuHasFocus = true;
            }
            else if(open)
            {
                foreach (MenuItem m in menuItemList)
                {
                    if (m.Bounds.Contains(e.Location))
                    {
                        m.OnMouseClick(e);
                        hasFocus = false;
                        //owner.MenuHasFocus = false;
                        Invalidate();
                    }
                    else
                    {
                        m.HasFocus = false;
                        //owner.MenuHasFocus = false;
                    }
                }
            }
        }

        public void OnMouseUp(System.Windows.Forms.MouseEventArgs e)
        {
            /* ignore */
        }

        public void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
        {
            /* ignore */
        }

        public void OnMouseMove(System.Windows.Forms.MouseEventArgs e)
        {
            /* ignore */
        }

        public void OnKeyPress(System.Windows.Forms.KeyPressEventArgs e)
        {
            //Invokes a Click event if the user hits ENTER
            //  while this component has focus
            if (e.KeyChar == '\r')
            {
                if (Click != null)
                {
                    Click(this, e);
                }
            }

            if (KeyPress != null)
            {
                KeyPress(this, e);
            }
        }

        public void OnKeyDown(System.Windows.Forms.KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Down)
            {
                if (open)
                {
                    if (index == -1)
                    {
                        index = 0;
                    }
                    else
                    {
                        menuItemList[index++].HasFocus = false; 
                    }
                    if (index > menuItemList.Count - 1)
                    {
                        index = 0;
                    }
                    menuItemList[index].HasFocus = true;
                    Invalidate();
                }
                else
                {
                    MenuSwap();
                    menuItemList[0].HasFocus = true;
                    Invalidate();
                }
            }
            else if (e.KeyCode == Keys.Up)
            {
                if (open)
                {
                    if (index == -1)
                    {
                        index = menuItemList.Count - 1;
                    }
                    else
                    {
                        menuItemList[index--].HasFocus = false;
                    }
                    if (index < 0)
                    {
                        index = menuItemList.Count - 1;
                    }
                    menuItemList[index].HasFocus = true;
                    Invalidate();
                }
                else
                {
                    MenuSwap();
                    menuItemList[menuItemList.Count - 1].HasFocus = true;
                    Invalidate();
                }
            }
            else if (e.KeyCode == Keys.Enter)
            {
                if (open)
                {
                    menuItemList[index].OnKeyDown(e);
                    hasFocus = false;
                    Invalidate();
                    //MenuSwap();
                }
                else
                {
                    MenuSwap();
                }
            }
        }

        private void Draw3DBorder(Graphics g, Rectangle r)
        {
            int PenWidth = (int)Pens.White.Width;

            g.DrawLine(Pens.PaleGoldenrod, new Point(r.Left, r.Top),
                new Point(r.Right - PenWidth, r.Top));
            g.DrawLine(Pens.PaleGoldenrod, new Point(r.Left, r.Top),
                new Point(r.Left, r.Bottom));
            g.DrawLine(Pens.DarkGoldenrod, new Point(r.Left, r.Bottom),
                new Point(r.Right - PenWidth, r.Bottom));
            g.DrawLine(Pens.DarkGoldenrod, new Point(r.Right - PenWidth, r.Bottom),
                new Point(r.Right - PenWidth, r.Top));
        }

        private void DrawFocus(Graphics g)
        {
            g.FillRectangle(Brushes.DarkKhaki, menuRectangle);
            int PenWidth = (int)Pens.White.Width;

            g.DrawLine(Pens.DarkGoldenrod, new Point(menuRectangle.Left, menuRectangle.Top),
                new Point(menuRectangle.Right - PenWidth, menuRectangle.Top));
            g.DrawLine(Pens.DarkGoldenrod, new Point(menuRectangle.Left, menuRectangle.Top),
                new Point(menuRectangle.Left, menuRectangle.Bottom));
            g.DrawLine(Pens.PaleGoldenrod, new Point(menuRectangle.Left, menuRectangle.Bottom),
                new Point(menuRectangle.Right - PenWidth, menuRectangle.Bottom));
            g.DrawLine(Pens.PaleGoldenrod, new Point(menuRectangle.Right - PenWidth, menuRectangle.Bottom),
                new Point(menuRectangle.Right - PenWidth, menuRectangle.Top));

            g.DrawString(text, font, Brushes.Black, (RectangleF)menuRectangle, labelFormat);
        }

        public void AddMenuItem(MenuItem m)
        {
            m.Owner = owner;
            m.ParentMenu = this;
            m.Bounds = new Rectangle(new Point(menuRectangle.X, container.Bottom), 
                new Size(menuRectangle.Width, menuRectangle.Height));
            container.Height += menuRectangle.Height;
            menusContainer.Height = container.Height - menuRectangle.Height;
            menuItemList.Add(m);
        }

        public void MenuSwap()
        {
            open = !open;
            foreach (MenuItem m in menuItemList)
            {
                if (open)
                {
                    m.Show();
                }
                else
                {
                    m.Hide();
                }
            }
            index = -1;
            Invalidate();
        }
    }
}
